/*******************************************************************************
 * Level Master 2000
 * Copyright (C) 2010 Christian Herzog
 * For more information see http://code.google.com/p/levelmaster2000/
 *
 * This file is licensed under the terms of the MIT license, see
 * http://www.opensource.org/licenses/mit-license.php for further information.
 ******************************************************************************/

#include "PropertyBrowser.h"
#include "ui_PropertyBrowser.h"

#include "commands/PropertyChangeCommand.h"

#include <QCompleter>
#include <QStandardItemModel>
#include <QModelIndex>
#include <QUndoStack>

//PropertyBrowser:
//#include "QtBoolPropertyManager.h"
#include "QtEditorFactory.h"
#include "QtGroupBoxPropertyBrowser.h"
//#include "QtIntPropertyManager.h"
#include "QtPropertyManager.h"
#include "QtTreePropertyBrowser.h"
#include "QtVariantProperty.h"

PropertyBrowser::PropertyBrowser( QWidget *parent, Communicator *communicator, QUndoStack *undoStack )
    : QDockWidget( parent ),
    communicator( communicator ),
    undoStack( undoStack )
{
    init();
}

void PropertyBrowser::init()
{
    Ui::propertyBrowserWidget uiPropertyBrowserWidget;
    uiPropertyBrowserWidget.setupUi( this );

    //Create the actual property browser:
    //If you want a QtGroupBoxPropertyBrowser, just swap these two.
    propertyBrowser = new QtTreePropertyBrowser( this );
    //propertyBrowser = new QtGroupBoxPropertyBrowser( propertyBrowserWidget );

    //Setting up the property managers:
    groupManager    = new QtGroupPropertyManager( this );
    doubleManager   = new QtDoublePropertyManager( this );
    stringManager   = new QtStringPropertyManager( this );
    pointManager    = new QtPointFPropertyManager( this );
    intManager      = new QtIntPropertyManager( this );
    boolManager     = new QtBoolPropertyManager( this );

    connect( doubleManager,
             SIGNAL( valueChanged( QtProperty*, double, double ) ),
             this,
             SLOT( valueChanged( QtProperty*, double, double ) ) );

    //TODO: look at SIGNALS...we only want to call valueChanged when ENTER is hit or focus lost..
    connect( stringManager,
             SIGNAL( valueChanged( QtProperty*, const QString&, const QString& ) ),
             this,
             SLOT( valueChanged( QtProperty*, const QString&, const QString& ) ) );

    connect( pointManager,
             SIGNAL( valueChanged( QtProperty*, const QPointF&, const QPointF& ) ),
             this,
             SLOT( valueChanged( QtProperty*, const QPointF&, const QPointF& ) ) );

    connect( intManager,
             SIGNAL( valueChanged( QtProperty*, const int, const int ) ),
             this,
             SLOT( valueChanged( QtProperty*, const int, const int ) ) );

    connect( boolManager,
             SIGNAL( valueChanged( QtProperty*, const bool, const bool ) ),
             this,
             SLOT( valueChanged( QtProperty*, const bool, const bool ) ) );

    //Create the factories:
    QtDoubleSpinBoxFactory *doubleSpinBoxFactory = new QtDoubleSpinBoxFactory( this );
    QtSpinBoxFactory *spinBoxFactory = new QtSpinBoxFactory( this );
    QtCheckBoxFactory *checkBoxFactory = new QtCheckBoxFactory( this );
    //QtSpinBoxFactory *spinBoxFactory = new QtSpinBoxFactory( this );
    QtLineEditFactory *lineEditFactory = new QtLineEditFactory( this );
    //QtEnumEditorFactory *comboBoxFactory = new QtEnumEditorFactory( this );

    //Set the factories:
    propertyBrowser->setFactoryForManager( doubleManager, doubleSpinBoxFactory );
    propertyBrowser->setFactoryForManager( stringManager, lineEditFactory );
    propertyBrowser->setFactoryForManager( pointManager->subDoublePropertyManager(), doubleSpinBoxFactory );
    propertyBrowser->setFactoryForManager( intManager, spinBoxFactory );
    propertyBrowser->setFactoryForManager( boolManager, checkBoxFactory );
    this->setWidget( propertyBrowser );
}


void PropertyBrowser::clearProperties()
{
    QList<QtProperty *> props = propertyBrowser->properties();

    QList<QtProperty *>::ConstIterator itProp = props.constBegin();
    while( itProp != props.constEnd() )
    {
        delete *itProp;
        itProp++;
    }
}

void PropertyBrowser::onComponentData( QString name, QMap<QString, QVariant> &properties )
{
    parsingEntityData = true;

    QtProperty *group;
    QtProperty *property;

    group = groupManager->addProperty( name );
    propertyBrowser->addProperty( group );

    QMap<QString, QVariant>::ConstIterator kvp = properties.constBegin();
    while( kvp != properties.constEnd() )
    {
        switch( kvp.value().type() )
        {
        case QVariant::Int:
            property = intManager->addProperty( kvp.key() );
            propertyToComponentName[property] = name;
            intManager->setValue( property, kvp.value().toInt() );
            intManager->setRange( property, -20000, 20000 );
            break;

        case QVariant::PointF:
            property = pointManager->addProperty( kvp.key() );
            propertyToComponentName[property] = name;
            pointManager->setValue( property, kvp.value().toPointF() );
            break;

        case QVariant::String:
            property = stringManager->addProperty( kvp.key() );
            propertyToComponentName[property] = name;
            stringManager->setValue( property, kvp.value().toString() );
            break;

        case QVariant::Double:
            property = doubleManager->addProperty( kvp.key() );
            propertyToComponentName[property] = name;
            doubleManager->setValue( property, kvp.value().toDouble() );
            break;

        case QVariant::Bool:
            property = boolManager->addProperty( kvp.key() );
            propertyToComponentName[property] = name;
            boolManager->setValue( property, kvp.value().toBool() );
            break;

        default:
            Logger::instance()->addLogMessage(Logger::LL_WARNING, "PropertyBrowser", "unsuported QVariant");
            break;
        }

        //Sort properties alphabetically:
        QtProperty* foundP = 0;
        foreach( QtProperty* p, group->subProperties() )
        {
            if( p->propertyName().localeAwareCompare( property->propertyName() ) < 0 )
            {
                foundP = p;
            }
        }

        group->insertSubProperty( property, foundP );

        kvp++;
    }

    parsingEntityData = false;
}

void PropertyBrowser::valueChanged( QtProperty *property, double value, double oldValue )
{
    if( !parsingEntityData )
    {
        communicator->sendUpdateComponentProperty( propertyToComponentName[property],
                                                   property->propertyName(),
                                                   QString::number( value ) );

        if( !LevelMaster2000::DISALLOW_NEW_COMMAND )
        {
            undoStack->push(
                    new PropertyChangeCommand<float, QtDoublePropertyManager>(
                            groupManager,
                            doubleManager,
                            propertyToComponentName[property],
                            property,
                            oldValue ) );
        }
    }
}

void PropertyBrowser::valueChanged( QtProperty *property, const QString &value, const QString &oldValue  )
{
    if( !parsingEntityData )
    {
        communicator->sendUpdateComponentProperty( propertyToComponentName[property],
                                                   property->propertyName(),
                                                   value );

        if( !LevelMaster2000::DISALLOW_NEW_COMMAND )
        {
            undoStack->push(
                    new PropertyChangeCommand<QString, QtStringPropertyManager>(
                            groupManager,
                            stringManager,
                            propertyToComponentName[property],
                            property,
                            oldValue ) );
        }
    }
}

void PropertyBrowser::valueChanged( QtProperty *property, const QPointF &value, const QPointF &oldValue )
{
    if( !parsingEntityData )
    {
        communicator->sendUpdateComponentProperty( propertyToComponentName[property],
                                                   property->propertyName(),
                                                   QString::number( value.x() ) + "/" + QString::number( value.y() ) );

        if( !LevelMaster2000::DISALLOW_NEW_COMMAND )
        {
            undoStack->push(
                    new PropertyChangeCommand<QPointF, QtPointFPropertyManager>(
                            groupManager,
                            pointManager,
                            propertyToComponentName[property],
                            property,
                            oldValue ) );
        }
    }
}

void PropertyBrowser::valueChanged( QtProperty *property, const bool value, const bool oldValue )
{
    if( !parsingEntityData )
    {
        QString val;
        if( value )
        {
            val = "true";
        }
        else
        {
            val = "false";
        }
        communicator->sendUpdateComponentProperty( propertyToComponentName[property],
                                                   property->propertyName(),
                                                   val );

        if( !LevelMaster2000::DISALLOW_NEW_COMMAND )
        {
            undoStack->push(
                    new PropertyChangeCommand<bool, QtBoolPropertyManager>(
                            groupManager,
                            boolManager,
                            propertyToComponentName[property],
                            property,
                            oldValue ) );
        }
    }
}

void PropertyBrowser::valueChanged( QtProperty *property, const int value, const int oldValue )
{
    if( !parsingEntityData )
    {

        communicator->sendUpdateComponentProperty( propertyToComponentName[property],
                                                   property->propertyName(),
                                                   QString::number( value ) );

        if( !LevelMaster2000::DISALLOW_NEW_COMMAND )
        {
            undoStack->push(
                    new PropertyChangeCommand<int, QtIntPropertyManager>(
                            groupManager,
                            intManager,
                            propertyToComponentName[property],
                            property,
                            oldValue ) );
        }
    }
}
